package com.studerb.view.connectionPool;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.swing.table.AbstractTableModel;

import org.apache.log4j.Logger;
import org.bushe.swing.event.EventTopicSubscriber;
import org.springframework.beans.factory.annotation.Autowired;

import com.studerb.events.NntpConnectionEvent;
import com.studerb.events.NntpConnectionEvent.EventType;
import com.studerb.events.listener.ConnectionPoolTableEL;
import com.studerb.nntp.NntpConnection;
import com.studerb.service.EventService;

public class ConnectionPoolTableModel extends AbstractTableModel {
	final protected static String[] columnNames = new String[] { "Server", "State", "Group", "Last Command", "Last Reply" };
	final protected static boolean[] isEditable = new boolean[] { false, false };
	final protected static Class[] columnClasses = new Class[] { String.class, Boolean.class, String.class, String.class, String.class };
	@Autowired protected EventService eventService;
	protected List<NntpConnection> connections;

	protected List<NntpConnectionEL> connectionListeners;

	Logger logger = Logger.getLogger(ConnectionPoolTableModel.class);
	ConnectionPoolTableEL connectionPoolTableEL;

	public synchronized void addConnection(NntpConnection connection) {
		if (connections.contains(connection)) {
			throw new IllegalArgumentException("Conn: " + connection + " already added to model");
		}
		this.connections.add(connection);
		int index = connections.indexOf(connection);
		NntpConnectionEL connListener = new NntpConnectionEL(this, connection);
		connectionListeners.add(index, connListener);
		//connection.getPropertyChangeSupport().addPropertyChangeListener(this);
		fireTableRowsInserted(index, index);
	}

	protected void connectionChange(NntpConnection conn, int column) {
		int index = connections.indexOf(conn);
		if (index < 0)
			throw new IllegalArgumentException("Bad Connection: " + conn);
		fireTableCellUpdated(index, column);
	}

	@Override public Class<?> getColumnClass(int columnIndex) {
		return columnClasses[columnIndex];
	}

	@Override public int getColumnCount() {
		return columnNames.length;
	}

	@Override public String getColumnName(int columnIndex) {
		return columnNames[columnIndex];
	}

	@Override public int getRowCount() {
		return connections.size();
	}

	@Override public Object getValueAt(int rowIndex, int columnIndex) {
		NntpConnection connection = connections.get(rowIndex);
		Column column = Column.values()[columnIndex];
		switch (column) {
		case GROUP:
			return connection.getCurrentGroup();
		case SERVER:
			return connection.getServer().getHost();
		case LAST_COMMAND:
			return connection.getLastCommand();
		case LAST_REPLY:
			return connection.getReplyString();
		case STATE:
			return connection.isConnected() == true;
		default:
			throw new IllegalArgumentException("Column[GROUP, SERVER, STATE, LAST_COMMAND/REPLY]: " + columnIndex + " not supported");
		}
	}

	public void init() {
		logger.debug("Initializing connectionPoolTableMonitor");
		connections = Collections.synchronizedList(new ArrayList<NntpConnection>());
		connectionListeners = new ArrayList<NntpConnectionEL>();
		connectionPoolTableEL = new ConnectionPoolTableEL(this);
	}

	@Override public boolean isCellEditable(int rowIndex, int columnIndex) {
		return isEditable[columnIndex];
	}

	public synchronized void removeConnection(NntpConnection connection) {
		int connIndex = connections.indexOf(connection);
		if (connIndex == -1) {
			logger.warn("connections does not contain : " + connection.toString() + "\tIgnoring Connection");
			return;
		}
		this.connections.remove(connection);
		this.connectionListeners.remove(connIndex);
		// t.getPropertyChangeSupport().removePropertyChangeListener(this);
		fireTableRowsDeleted(connIndex, connIndex);
	}

	@Override public void setValueAt(Object value, int rowIndex, int columnIndex) {
		throw new UnsupportedOperationException("Connection Pool Table does not have editable table cells");
	}

	public enum Column {
		SERVER, STATE, GROUP, LAST_COMMAND, LAST_REPLY
	}
}

class NntpConnectionEL implements EventTopicSubscriber {
	private final Logger logger = Logger.getLogger(NntpConnectionEL.class);
	private final NntpConnection connection;
	private final ConnectionPoolTableModel parent;
	private final String topic;

	public NntpConnectionEL(ConnectionPoolTableModel parent, NntpConnection conn) {
		logger.debug("NntpConnectionEventListener constructor for conn: " + conn);
		this.parent = parent;
		this.connection = conn;
		this.topic = conn.getName();
		logger.debug("Subscribing to connection: " + conn.getName());
		parent.eventService.subscribe(topic, this);
	}

	@Override public void onEvent(String topic, Object data) {
		NntpConnectionEvent event = (NntpConnectionEvent) data;
		assert (event.getConnection() == this.connection);
		int columnIndex;
		EventType eventType = event.getType();
		switch (eventType) {
		case CONNECTED:
			columnIndex = ConnectionPoolTableModel.Column.STATE.ordinal();
			parent.connectionChange(this.connection, columnIndex);
			break;
		case DISCONNECTED:
			columnIndex = ConnectionPoolTableModel.Column.STATE.ordinal();
			parent.connectionChange(this.connection, columnIndex);
			break;
		case COMMAND:
			columnIndex = ConnectionPoolTableModel.Column.LAST_COMMAND.ordinal();
			parent.connectionChange(this.connection, columnIndex);
			columnIndex = ConnectionPoolTableModel.Column.LAST_REPLY.ordinal();
			parent.connectionChange(this.connection, columnIndex);
			break;
		case GROUP:
			columnIndex = ConnectionPoolTableModel.Column.GROUP.ordinal();
			parent.connectionChange(this.connection, columnIndex);
			break;
		case EXCEPTION:
			break;
		default:
			logger.error("Bad command type of NntpConnectionEvent: " + eventType);
		}
	}
}
